SearchProvider.prototype = {
_init: function(title) {
this.title = title;
+ this.searchSystem = null;
+ this.searchAsync = false;
+ },
+
+ _asyncCancelled: function() {
+ },
+
+ startAsync: function() {
+ this.searchAsync = true;
+ },
+
+ tryCancelAsync: function() {
+ if (!this.searchAsync)
+ return;
+ this._asyncCancelled();
+ this.searchAsync = false;
+ },
+
+ /**
+ * addItems:
+ * @items: an array of result identifier strings representing
+ * items which match the last given search terms.
+ *
+ * This should be used for something that requires a bit more
+ * logic; it's designed to be an asyncronous way to add a result
+ * to the current search.
+ */
+ addItems: function(items) {
+ if (!this.searchSystem)
+ throw new Error('Search provider not registered');
+
+ if (!items.length)
+ return;
+
+ this.tryCancelAsync();
+
+ this.searchSystem.addProviderItems(this, items);
},
/**
},
registerProvider: function (provider) {
+ provider.searchSystem = this;
this._providers.push(provider);
},
this._previousResults = [];
},
+ addProviderItems: function(provider, items) {
+ this.emit('search-updated', provider, items);
+ },
+
updateSearch: function(searchString) {
searchString = searchString.replace(/^\s+/g, '').replace(/\s+$/g, '');
if (searchString == '')
- return [];
+ return;
let terms = searchString.split(/\s+/);
+ this.updateSearchResults(terms);
+ },
+
+ updateSearchResults: function(terms) {
+ if (!terms)
+ return;
+
let isSubSearch = terms.length == this._previousTerms.length;
if (isSubSearch) {
for (let i = 0; i < terms.length; i++) {
let results = [];
if (isSubSearch) {
- for (let i = 0; i < this._previousResults.length; i++) {
+ for (let i = 0; i < this._providers.length; i++) {
let [provider, previousResults] = this._previousResults[i];
+ provider.tryCancelAsync();
try {
let providerResults = provider.getSubsearchResultSet(previousResults, terms);
- if (providerResults.length > 0)
- results.push([provider, providerResults]);
+ results.push([provider, providerResults]);
} catch (error) {
global.log ('A ' + error.name + ' has occured in ' + provider.title + ': ' + error.message);
}
} else {
for (let i = 0; i < this._providers.length; i++) {
let provider = this._providers[i];
+ provider.tryCancelAsync();
try {
let providerResults = provider.getInitialResultSet(terms);
- if (providerResults.length > 0)
- results.push([provider, providerResults]);
+ results.push([provider, providerResults]);
} catch (error) {
global.log ('A ' + error.name + ' has occured in ' + provider.title + ': ' + error.message);
}
this._previousTerms = terms;
this._previousResults = results;
-
- return results;
- }
+ this.emit('search-completed', results);
+ },
};
Signals.addSignalMethods(SearchSystem.prototype);
SearchResults.prototype = {
_init: function(searchSystem, openSearchSystem) {
this._searchSystem = searchSystem;
+ this._searchSystem.connect('search-updated', Lang.bind(this, this._updateCurrentResults));
+ this._searchSystem.connect('search-completed', Lang.bind(this, this._updateResults));
this._openSearchSystem = openSearchSystem;
this.actor = new St.BoxLayout({ name: 'searchResults',
this._selectedProvider = -1;
this._providers = this._searchSystem.getProviders();
this._providerMeta = [];
- for (let i = 0; i < this._providers.length; i++)
+ this._providerMetaResults = {};
+ for (let i = 0; i < this._providers.length; i++) {
this.createProviderMeta(this._providers[i]);
-
+ this._providerMetaResults[this.providers[i].title] = [];
+ }
this._searchProvidersBox = new St.BoxLayout({ style_class: 'search-providers-box' });
this.actor.add(this._searchProvidersBox);
}
},
+ _clearDisplayForProvider: function(index) {
+ let meta = this._providerMeta[index];
+ meta.resultDisplay.clear();
+ meta.actor.hide();
+ },
+
reset: function() {
this._searchSystem.reset();
this._statusText.hide();
this._statusText.show();
},
+ doSearch: function (searchString) {
+ this._searchSystem.updateSearch(searchString);
+ },
+
_metaForProvider: function(provider) {
return this._providerMeta[this._providers.indexOf(provider)];
},
- updateSearch: function (searchString) {
- let results = this._searchSystem.updateSearch(searchString);
-
- this._clearDisplay();
+ _updateCurrentResults: function(searchSystem, provider, results) {
+ let terms = searchSystem.getTerms();
+ let meta = this._metaForProvider(provider);
+ meta.resultDisplay.clear();
+ meta.actor.show();
+ meta.resultDisplay.renderResults(results, terms);
+ return true;
+ },
+ _updateResults: function(searchSystem, results) {
if (results.length == 0) {
this._statusText.set_text(_("No matching results."));
this._statusText.show();
this._statusText.hide();
}
- let terms = this._searchSystem.getTerms();
+ let terms = searchSystem.getTerms();
this._openSearchSystem.setSearchTerms(terms);
// To avoid CSS transitions causing flickering
for (let i = 0; i < results.length; i++) {
let [provider, providerResults] = results[i];
- let meta = this._metaForProvider(provider);
- meta.actor.show();
- meta.resultDisplay.renderResults(providerResults, terms);
+ if (providerResults.length == 0) {
+ this._clearDisplayForProvider(i);
+ } else {
+ this._providerMetaResults[provider.title] = providerResults;
+ this._clearDisplayForProvider(i);
+ let meta = this._metaForProvider(provider);
+ meta.actor.show();
+ meta.resultDisplay.renderResults(providerResults, terms);
+ }
}
if (this._selectedOpenSearchButton == -1)
_doSearch: function () {
this._searchTimeoutId = 0;
let text = this._text.get_text().replace(/^\s+/g, '').replace(/\s+$/g, '');
- this._searchResults.updateSearch(text);
+ this._searchResults.doSearch(text);
return false;
}